package com.netty;

import com.netty.coder.ObjectDecoder;
import com.netty.coder.ObjectEncoder;
import com.netty.handler.ImUdpServerHandler;
import com.netty.handler.LoggingHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.handler.stream.ChunkedWriteHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ThreadFactory;

/**
 * @author zl
 * @version 1.0
 * @date 2021/9/29 16:17
 */
public class ImUdpServer {

    private Logger logger = LoggerFactory.getLogger(getClass());

    private EventLoopGroup workerGroup;

    private CIMUdpRequestHandler cimRequestHandler;

    private int port;

    public ImUdpServer(CIMUdpRequestHandler cimRequestHandler, int port) {
        this.port = port;
        this.cimRequestHandler = cimRequestHandler;
    }

    /**
     * 启动netty udp
     *
     * @throws InterruptedException
     */
    public void startServer() throws InterruptedException {
        logger.info(">>>>>>>>>>>>>>>>>>>>>>start udp ...<<<<<<<<<<<<<<<<<<<<<<");
        createEventGroup();
        // Server 服务启动
        Bootstrap bootstrap = new Bootstrap();//udp不能使用ServerBootstrap
        bootstrap.group(workerGroup);
        bootstrap.channel(NioDatagramChannel.class)
                .option(ChannelOption.SO_BROADCAST,true)
                .option(ChannelOption.SO_RCVBUF, 2048 * 2048)// 设置UDP读缓冲区为2M
                .option(ChannelOption.SO_SNDBUF, 1024 * 1024)// 设置UDP写缓冲区为1M
                .option(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(65535))
                .handler(new ChannelInitializer<DatagramChannel>() {
                        @Override
                        public void initChannel(DatagramChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            //日志输出
                            pipeline.addLast(new LoggingHandler());
                            // 主要用于处理大数据流，比如一个1G大小的文件如果你直接传输肯定会撑暴jvm内存的; 增加之后就不用考虑这个问题了
                            pipeline.addLast(new ChunkedWriteHandler());
                            pipeline.addLast(new ObjectDecoder(Object::getClass));
                            pipeline.addLast(new ObjectEncoder());
                            pipeline.addLast("handler", new ImUdpServerHandler(cimRequestHandler));
                        }
                    });

        ChannelFuture channelFuture = bootstrap.bind(port).syncUninterruptibly();
        channelFuture.channel().newSucceededFuture().addListener(future -> {
            String logBanner = "\n\n" +
                    "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n" +
                    "*                                                                                   *\n" +
                    "*                                                                                   *\n" +
                    "*                   udp Server started on port {}.                           *\n" +
                    "*                                                                                   *\n" +
                    "*                                                                                   *\n" +
                    "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n";
            logger.info(logBanner, port);
        });
        channelFuture.channel().closeFuture().addListener(future -> this.destroy(workerGroup));
    }

    /**
     * 销毁队列
     */
    public void destroy(EventLoopGroup workerGroup) {
        logger.info(">>>>>>>>>>>>>>>>>>>>>>destroy server<<<<<<<<<<<<<<<<<<<<<<");
        workerGroup.shutdownGracefully();
        logger.info(">>>>>>>>>>>>>>>>>>>>>>destroy server complate.<<<<<<<<<<<<<<<<<<<<<<");
    }

    /**
     * 根据系统信息创建对应的队列
     */
    private void createEventGroup() {
        ThreadFactory workerThreadFactory = r -> {
            Thread thread = new Thread(r);
            thread.setName("nio-worker-work");
            return thread;
        };
        if (isLinuxSystem()) {
            workerGroup = new EpollEventLoopGroup();
        } else {
            workerGroup = new NioEventLoopGroup();
        }
    }

    /**
     * 判断是否是linux
     *
     * @return
     */
    private boolean isLinuxSystem() {
        String osName = System.getProperty("os.name").toLowerCase();
        return false;
    }

}
